package db;
/*
 * Use this class to wait for a notify() or notifyAll() until timeout occurs.
 * This allows threads to implement the semantics of waiting for something and give up if that something is not available within the duration specified in the timeout value.
 * 
 * The usage pattern is:
 * Waiter thread:
 * 
 * synchronized(syncObject)
 * {
 *   NotifyWaiter waiter=new NotifyWaiter(syncObject,timeout);
 *   while (some_condition==false)
 *   {
 *     if (waiter.waitForNotify()==false)
 *     {
 *        throw new TimeoutException(); //timeout occurred
 *     } 
 *   }
 * }
 * 
 * Notify thread:
 * synchronized(syncObject)
 * {
 *   some_condition=true;
 *   syncObject.notify();
 * }
 * 
 * Notes:
 * The timeout value must be relatively large to the time quantum returned by System.currentMillis().
 * 
 * Two cases to ponder about:
 * 1. Notify() could have been called within timeout duration, but due to timer inaccuracy System.currentTimeMillis()-start could evaluate to false.
 * Effectively, this case is treated as if notify() is called after timeout.
 * If timeout value is much larger than the timer inaccuracy, this behavior should not change application semantics.
 * 2. wait times out, but System.currentTimeMillis()-start evaluates to false.
 * From the application's perspective, this looks like a spurious wake-up, which an application must be able to handle for other spurious wake-up reasons.
 * There may be a slight inefficiency here as we re-evaluate everything again. 
 * 
 * Timeout value>100 ms should be safe for most JVM implementations.
 *  
 */

public class NotifyWaiter
{
    final private long start;
    final private Object object;
    final private long timeoutInMillis;
    public NotifyWaiter(Object object,long timeoutInMillis)
    {
        this.object=object;
        this.start=System.currentTimeMillis();
        this.timeoutInMillis=timeoutInMillis;
    }
    public boolean waitForNotify()
    {
        long amount=timeoutInMillis-(System.currentTimeMillis()-start);
        if (amount<=0)
        {
            return false;
        }
        try
        {
            object.wait(amount);
            return System.currentTimeMillis()-start<timeoutInMillis;
        }
        catch (InterruptedException e)
        {
            return false;
        }
    }
    public boolean waitForNotifyDisregardTimeout()
    {
        try
        {
            object.wait();
            return true;
        }
        catch (InterruptedException e)
        {
            return false;
        }
    }
}
